---
title: InfiniteScrollArea
description: "`InfiniteScrollArea`は、無限スクロール機能を提供するコンポーネントです。このコンポーネントは、コンポーネントの終わりに達したときに次のデータセットを自動的に読み込み、表示することで、スムーズなスクロール体験を提供します。"
storybook: components-infinitescrollarea--basic
source: components/infinite-scroll-area
style: components/infinite-scroll-area/infinite-scroll-area.style.ts
---

```tsx preview functional iframe client
const [count, setCount] = useState<number>(50)

return (
  <InfiniteScrollArea
    loading={<Loading.Oval fontSize="2xl" />}
    onLoad={({ finish, index }) => {
      console.log("load", index)
      setCount((prev) => prev + 50)
      if (index >= 5) finish()
    }}
  >
    {Array(count)
      .fill(0)
      .map((_, index) => (
        <Card.Root key={index}>
          <Card.Header>
            <Heading size="xl">天元突破グレンラガン</Heading>
          </Card.Header>

          <Card.Body>
            <Text>
              いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
            </Text>
          </Card.Body>
        </Card.Root>
      ))}
  </InfiniteScrollArea>
)
```

## 使い方

:::code-group

```tsx [package]
import { InfiniteScrollArea } from "@yamada-ui/react"
```

```tsx [alias]
import { InfiniteScrollArea } from "@/components/ui"
```

```tsx [monorepo]
import { InfiniteScrollArea } from "@workspace/ui"
```

:::

```tsx
<InfiniteScrollArea />
```

### ビューポートを指定する

ビューポートを指定する場合は、`rootRef`に`ref`を設定します。

:::note
`rootRef`が設定されない場合は、ブラウザのビューポートが使用されます。
:::

```tsx preview functional iframe client
const rootRef = useRef<HTMLDivElement>(null)
const [count, setCount] = useState<number>(50)
const resetRef = useRef<() => void>(noop)

return (
  <VStack h="full" alignItems="flex-start">
    <VStack
      ref={rootRef}
      borderWidth="1px"
      overflowY="auto"
      p="lg"
      h="sm"
      rounded="l2"
    >
      <InfiniteScrollArea
        loading={<Loading.Oval fontSize="2xl" />}
        resetRef={resetRef}
        rootRef={rootRef}
        onLoad={({ finish, index }) => {
          console.log("load", index)
          setCount((prev) => prev + 50)
          if (index >= 5) finish()
        }}
      >
        {Array(count)
          .fill(0)
          .map((_, index) => (
            <Card.Root key={index}>
              <Card.Header>
                <Heading size="xl">天元突破グレンラガン</Heading>
              </Card.Header>

              <Card.Body>
                <Text>
                  いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
                </Text>
              </Card.Body>
            </Card.Root>
          ))}
      </InfiniteScrollArea>
    </VStack>

    <Button
      onClick={() => {
        setCount(50)
        resetRef.current()
      }}
    >
      Reset
    </Button>
  </VStack>
)
```

### rootMarginを設定する

[rootMargin](https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API#rootmargin)を設定する場合は、`rootMargin`に文字列を設定します。

```tsx preview functional iframe client
const [count, setCount] = useState<number>(50)

return (
  <InfiniteScrollArea
    rootMargin="300px 0px 0px 0px"
    loading={<Loading.Oval fontSize="2xl" />}
    onLoad={({ finish, index }) => {
      console.log("load", index)
      setCount((prev) => prev + 50)
      if (index >= 5) finish()
    }}
  >
    {Array(count)
      .fill(0)
      .map((_, index) => (
        <Card.Root key={index}>
          <Card.Header>
            <Heading size="xl">天元突破グレンラガン</Heading>
          </Card.Header>

          <Card.Body>
            <Text>
              いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
            </Text>
          </Card.Body>
        </Card.Root>
      ))}
  </InfiniteScrollArea>
)
```

### thresholdを設定する

[threshold](https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API#threshold)を設定する場合は、`threshold`に数値を設定します。

```tsx preview functional iframe client
const [count, setCount] = useState<number>(50)

return (
  <InfiniteScrollArea
    threshold={1}
    loading={<Loading.Oval fontSize="2xl" />}
    onLoad={({ finish, index }) => {
      console.log("load", index)
      setCount((prev) => prev + 50)
      if (index >= 5) finish()
    }}
  >
    {Array(count)
      .fill(0)
      .map((_, index) => (
        <Card.Root key={index}>
          <Card.Header>
            <Heading size="xl">天元突破グレンラガン</Heading>
          </Card.Header>

          <Card.Body>
            <Text>
              いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
            </Text>
          </Card.Body>
        </Card.Root>
      ))}
  </InfiniteScrollArea>
)
```

### 初回に読み込む

初回に読み込む場合は、`initialLoad`を`true`に設定します。デフォルトでは、`initialLoad`が`false`に設定されており、初回(`index=0`)の`onLoad`は実行されません。

`true`: スクロール量に関わらず、初回の`onLoad`は実行され、提供される`index`は`0`から始まります。\
`false`: スクロールが一定に達すると`onLoad`が実行され、提供される`index`は`1`から始まります。

```tsx preview functional iframe client
const [count, setCount] = useState<number>(50)

return (
  <InfiniteScrollArea
    initialLoad
    loading={<Loading.Oval fontSize="2xl" />}
    onLoad={({ finish, index }) => {
      console.log("load", index)
      setCount((prev) => prev + 50)
      if (index >= 5) finish()
    }}
  >
    {Array(count)
      .fill(0)
      .map((_, index) => (
        <Card.Root key={index}>
          <Card.Header>
            <Heading size="xl">天元突破グレンラガン</Heading>
          </Card.Header>

          <Card.Body>
            <Text>
              いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
            </Text>
          </Card.Body>
        </Card.Root>
      ))}
  </InfiniteScrollArea>
)
```

### 開始するindexを変更する

開始する`index`を変更する場合は、`startIndex`に数値を設定します。デフォルトでは、`1`が設定されています。

```tsx preview functional iframe client
const [count, setCount] = useState<number>(50)

return (
  <InfiniteScrollArea
    startIndex={3}
    loading={<Loading.Oval fontSize="2xl" />}
    onLoad={({ finish, index }) => {
      console.log("load", index)
      setCount((prev) => prev + 50)
      if (index >= 5) finish()
    }}
  >
    {Array(count)
      .fill(0)
      .map((_, index) => (
        <Card.Root key={index}>
          <Card.Header>
            <Heading size="xl">天元突破グレンラガン</Heading>
          </Card.Header>

          <Card.Body>
            <Text>
              いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
            </Text>
          </Card.Body>
        </Card.Root>
      ))}
  </InfiniteScrollArea>
)
```

### 方向を変更する

方向を変更する場合は、`orientation`に`"vertical"`または`"horizontal"`を設定します。デフォルトは、`"vertical"`です。

```tsx preview functional iframe client
const [count, setCount] = useState<number>(50)

return (
  <>
    {Array(count)
      .fill(0)
      .map((_, index) => (
        <Image
          borderWidth="1px"
          rounded="l2"
          key={index}
          src="https://gurren-lagann15th.com/assets/img/top/visual.jpg"
          w="sm"
          h="full"
          objectFit="cover"
        />
      ))}
  </>
)
```

### 反転させる

反転させる場合は、`reverse`を`true`に設定します。デフォルトは、`false`です。

```tsx preview functional iframe client
const rootRef = useRef<HTMLDivElement>(null)
const [count, setCount] = useState<number>(50)

return (
  <Box ref={rootRef} h="full" overflow="auto">
    <InfiniteScrollArea
      reverse
      rootRef={rootRef}
      loading={<Loading.Oval fontSize="2xl" />}
      onLoad={({ finish, index }) => {
        console.log("load", index)
        setCount((prev) => prev + 50)
        if (index >= 5) finish()
      }}
    >
      {Array(count)
        .fill(0)
        .map((_, index) => (
          <Card.Root key={index}>
            <Card.Header>
              <Heading size="xl">天元突破グレンラガン</Heading>
            </Card.Header>

            <Card.Body>
              <Text>
                いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
              </Text>
            </Card.Body>
          </Card.Root>
        ))}
    </InfiniteScrollArea>
  </Box>
)
```

### 無効にする

無効にする場合は、`disabled`を`true`に設定します。これは、特定の条件下でスクロールに合わせて`onLoad`を実行したくない場合に有効です。

```tsx preview functional iframe client
const [disabled, setDisabled] = useState<boolean>(false)
const [count, setCount] = useState<number>(50)

return (
  <VStack h="full" alignItems="flex-start">
    <InfiniteScrollArea
      disabled={disabled}
      loading={<Loading.Oval fontSize="2xl" />}
      onLoad={({ finish, index }) => {
        console.log("load", index)
        setCount((prev) => prev + 50)
        if (index >= 5) finish()
      }}
      borderWidth="1px"
      h="sm"
      overflowY="auto"
      p="lg"
      rounded="l2"
    >
      {Array(count)
        .fill(0)
        .map((_, index) => (
          <Card.Root key={index}>
            <Card.Header>
              <Heading size="xl">天元突破グレンラガン</Heading>
            </Card.Header>

            <Card.Body>
              <Text>
                いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
              </Text>
            </Card.Body>
          </Card.Root>
        ))}
    </InfiniteScrollArea>

    <Button
      colorScheme={disabled ? "success" : "danger"}
      onClick={() => setDisabled((prev) => !prev)}
    >
      {disabled ? "Enable" : "Disabled"}
    </Button>
  </VStack>
)
```

### リセットする

`index`をリセットする場合は、`resetRef`に`ref`を設定します。設定された`ref`にコールバック関数が付与されるので、それを実行します。

:::warning
`resetRef`のコールバックを実行すると、スクロールもリセットされます。
:::

```tsx preview functional iframe client
const resetRef = useRef<() => void>(noop)
const [count, setCount] = useState<number>(50)

return (
  <VStack h="full" alignItems="flex-start">
    <InfiniteScrollArea
      resetRef={resetRef}
      loading={<Loading.Oval fontSize="2xl" />}
      onLoad={({ finish, index }) => {
        console.log("load", index)
        setCount((prev) => prev + 50)
        if (index >= 5) finish()
      }}
      borderWidth="1px"
      h="sm"
      overflowY="auto"
      p="lg"
      rounded="l2"
    >
      {Array(count)
        .fill(0)
        .map((_, index) => (
          <Card.Root key={index}>
            <Card.Header>
              <Heading size="xl">天元突破グレンラガン</Heading>
            </Card.Header>

            <Card.Body>
              <Text>
                いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
              </Text>
            </Card.Body>
          </Card.Root>
        ))}
    </InfiniteScrollArea>

    <Button
      onClick={() => {
        setCount(50)
        resetRef.current()
      }}
    >
      Reset
    </Button>
  </VStack>
)
```

### スクロールの終わりに要素を表示する

スクロールの終わりに要素を表示する場合は、`finish`に`ReactNode`を設定します。

```tsx preview functional iframe client
const [count, setCount] = useState<number>(50)

return (
  <InfiniteScrollArea
    finish={<>俺のドリルは！天を作るドリルだああああっ！！</>}
    loading={<Loading.Oval fontSize="2xl" />}
    onLoad={({ finish, index }) => {
      console.log("load", index)
      setCount((prev) => prev + 50)
      if (index >= 1) finish()
    }}
  >
    {Array(count)
      .fill(0)
      .map((_, index) => (
        <Card.Root key={index}>
          <Card.Header>
            <Heading size="xl">天元突破グレンラガン</Heading>
          </Card.Header>

          <Card.Body>
            <Text>
              いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
            </Text>
          </Card.Body>
        </Card.Root>
      ))}
  </InfiniteScrollArea>
)
```

### トリガーをカスタマイズする

トリガーをカスタマイズする場合は、`triggerProps`にpropsを設定します。

```tsx preview functional iframe client
const [count, setCount] = useState<number>(50)

return (
  <InfiniteScrollArea
    triggerProps={{
      bg: "primary.50",
      p: "md",
      rounded: "md",
    }}
    loading={<Loading.Oval fontSize="2xl" />}
    onLoad={async ({ finish, index }) => {
      console.log("load", index)
      await wait(5000)
      setCount((prev) => prev + 50)
      if (index >= 5) finish()
    }}
  >
    {Array(count)
      .fill(0)
      .map((_, index) => (
        <Card.Root key={index}>
          <Card.Header>
            <Heading size="xl">天元突破グレンラガン</Heading>
          </Card.Header>

          <Card.Body>
            <Text>
              いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
            </Text>
          </Card.Body>
        </Card.Root>
      ))}
  </InfiniteScrollArea>
)
```

### ローディングをカスタマイズする

ローディングをカスタマイズする場合は、`loading`に`ReactNode`を設定します。

```tsx preview functional iframe client
const [count, setCount] = useState<number>(50)

return (
  <InfiniteScrollArea
    loading={<>一回転すればほんの少しだけ前に進む、それがドリルなんだよ！！</>}
    onLoad={async ({ finish, index }) => {
      console.log("load", index)
      await wait(5000)
      setCount((prev) => prev + 50)
      if (index >= 5) finish()
    }}
  >
    {Array(count)
      .fill(0)
      .map((_, index) => (
        <Card.Root key={index}>
          <Card.Header>
            <Heading size="xl">天元突破グレンラガン</Heading>
          </Card.Header>

          <Card.Body>
            <Text>
              いいか、忘れんな。お前を信じろ。俺が信じるお前でもない。お前が信じる俺でもない。お前が信じる…お前を信じろ！
            </Text>
          </Card.Body>
        </Card.Root>
      ))}
  </InfiniteScrollArea>
)
```

## Props

<PropsTable name="infinite-scroll-area" />
